package de.idyl.crypto.zip.impl;

import java.util.Calendar;
import java.util.Date;
import java.util.zip.ZipEntry;

/**
 * information about one zip entry that is written
 * to an encrypted zip archive or read from one
 *
 * @author <a href="mailto:olaf@merkert.de">Olaf Merkert</a>
 */
public class ExtZipEntry extends ZipEntry {

	/** empty instance with only a name */
	public ExtZipEntry( String name ) {
		super( name );
	}

	/** copy all "non-compression" attributes */
	public ExtZipEntry( ExtZipEntry entry ) {
		super( entry.getName() );
		setCompressedSize( entry.getCompressedSize() );
		setSize( entry.getSize() );
		setComment( entry.getComment() );
		setTime( entry.getTime() );
	}

	public void initEncryptedEntry() {
		setCrc( 0 );	// CRC-32 / for encrypted files it's 0 as AES/MAC checks integritiy

		this.flag |= 1;  // bit0 - encrypted
    //flag |= 8;  // bit3 - use data descriptor

		this.primaryCompressionMethod = 0x63;

		byte[] extraBytes = new byte[11];
		extraBytes = new byte[11];

		// extra data header ID for AES encryption is 0x9901
		extraBytes[0] = 0x01;
		extraBytes[1] = (byte)0x99;

		// data size (currently 7, but subject to possible increase in the future)
		extraBytes[2] = 0x07;	// data size
		extraBytes[3] = 0x00;	// data size

		// Integer version number specific to the zip vendor
		extraBytes[4]	= 0x02;	// version number
		extraBytes[5]	= 0x00;	// version number

		// 2-character vendor ID
		extraBytes[6] = 0x41;	// vendor id
		extraBytes[7]	= 0x45;	// vendor id

		// AES encryption strength	- 1=128, 2=192, 3=256
		extraBytes[8]	= 0x03;

		// actual compression method - 0x0000==stored (no compression) - 2 bytes
		extraBytes[9]	= (byte)(getMethod() & 0xff);
		extraBytes[10]= (byte)((getMethod() & 0xff00) >> 8);

		setExtra( extraBytes );
	}

	/*
  public boolean useDataDescriptor() {
    return ((flag & 8) == 8);
  }
  */

  protected int flag;

  public int getFlag() {
    return this.flag;
  }

  public boolean isEncrypted() {
  	return (flag & 1) > 0;
  }

  protected int offset;

  public int getOffset() {
  	return offset;
  }

  public void setOffset( int offset ) {
  	this.offset = offset;
  }

  // 0x63 for encryption
  protected int primaryCompressionMethod;

	public int getPrimaryCompressionMethod() {
		return primaryCompressionMethod;
	}

	public void setPrimaryCompressionMethod( int primaryCompressionMethod ) {
		this.primaryCompressionMethod = primaryCompressionMethod;
	}

	/**
	 * Encrypted files: Note that the value in the "compressed size" fields
	 * of the local file header and the central directory entry is the total
	 * size of all the items listed above. In other words, it is the total
	 * size of the salt value, password verification value, encrypted data,
	 * and authentication code.
	 *
	 * @return data size only
	 */
	public long getEncryptedDataSize() {
		// authentication (10), salt (16), verification (2)
		return getCompressedSize() - 10 - 16 - 2;
	}

  // --------------------------------------------------------------------------

  public long getDosTime() {
    return javaToDosTime( getTime() );
  }

  protected static long javaToDosTime(long time) {
    Date d = new Date(time);
    Calendar ca = Calendar.getInstance();
    ca.setTime( d );
    int year = ca.get( Calendar.YEAR );
    if (year < 1980) {
      return (1 << 21) | (1 << 16);
    }
    return (year - 1980) << 25
        | (ca.get(Calendar.MONTH) + 1) << 21
        | ca.get(Calendar.DAY_OF_MONTH) << 16
        | ca.get(Calendar.HOUR_OF_DAY) << 11
        | ca.get(Calendar.MINUTE) << 5
        | ca.get(Calendar.SECOND) >> 1;
  }

}
